package com.gitee.wsl.doc.prosemirror.model

import com.gitee.wsl.doc.prosemirror.exception.RangeError
import com.gitee.wsl.doc.prosemirror.plugin.Plugin
import com.gitee.wsl.doc.prosemirror.plugin.StateField
import com.gitee.wsl.doc.prosemirror.state.FieldDesc
import com.gitee.wsl.doc.prosemirror.state.selection.Selection
import com.gitee.wsl.doc.prosemirror.state.selection.TextSelection

// Object wrapping the part of a state object that stays the same
// across transactions. Stored in the state's `config` property.
class Configuration(val schema: Schema, plugins: List<Plugin<*>>? = null) {
    val plugins: List<Plugin<*>> = plugins ?: emptyList()
    val fields: List<FieldDesc<out Any?>>
    val pluginsByKey: Map<String, Plugin<*>>

    init {
        val byKey = mutableMapOf<String, Plugin<*>>()
        this.plugins.forEach { plugin ->
            if (byKey[plugin.key] != null) {
                throw RangeError("Adding different instances of a keyed plugin (" + plugin.key + ")")
            }
            byKey[plugin.key] = plugin
        }
        pluginsByKey = byKey.toMap()

        fields = baseFields + this.plugins.mapNotNull { plugin ->
            if (plugin.spec.state != null) {
                FieldDesc(plugin.key, plugin.spec.state as StateField<Any>)
            } else {
                null
            }
        }
    }

    companion object {
        val baseFields: List<FieldDesc<out Any?>> = listOf(
            FieldDesc(
                name = "doc",
                init = { config, state -> config.doc ?: config.schema!!.topNodeType.createAndFill()!! },
                apply = { tr, value, oldState, newState -> tr.doc }
            ),

            FieldDesc(
                name = "selection",
                init = { config, state -> config.selection ?: Selection.atStart(state.doc) },
                apply = { tr, value, oldState, newState -> tr.selection }
            ),

            FieldDesc(
                name = "storedMarks",
                init = { config, state -> config.storedMarks },
                apply = { tr, _marks, _old, state ->
                    if ((state.selection as? TextSelection)?._cursor != null) tr.storedMarks else null
                }
            ),

            FieldDesc(
                "scrollToSelection",
                init = { config, state -> 0 },
                apply = { tr, value, oldState, newState -> if (tr.scrolledIntoView) value + 1 else value }
            )
        )
    }
}